#pragma once
#include <assert.h>
#ifdef _WIN32
#define USE_CRITICAL_SECTION_
#include <windows.h>
#include <mutex>
#ifdef USE_CRITICAL_SECTION
#define MUTEX CRITICAL_SECTION
#define CONDITION CONDITION_VARIABLE
#else
#define MUTEX std::mutex
#define CONDITION std::condition_variable
#endif // USE_CRITICAL_SECTION
#else
#include <mutex>
#include <sys/time.h>
#define MUTEX pthread_mutex_t
#define CONDITION pthread_cond_t
#endif


static int Init_Mutex(MUTEX &mutex)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	InitializeCriticalSection(&mutex);
#else
	//nothing to do
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_mutex_init(&mutex, nullptr);
    if(ret) assert(false);
#endif
	return 0;
}
static int Init_Condition(CONDITION &condition)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	InitializeConditionVariable(&condition);
#else
	//nothing to do
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_cond_init(&condition, nullptr);
	if (ret) assert(false);
#endif
	return 0;
}

static int Destroy_Mutex(MUTEX &mutex)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	DeleteCriticalSection(&mutex);
#else
	//nothing to do
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_mutex_destroy(&mutex);
	if (ret) assert(false);
#endif
	return 0;
}

static int Destroy_Condition(CONDITION &condition)
{
#ifdef _WIN32
	//noting to do
#else
    int ret= pthread_cond_destroy(&condition);
	if (ret) assert(false);
#endif
	return 0;
}

static int Mutex_Lock(MUTEX &mutex)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	EnterCriticalSection(&mutex);
#else
	mutex.lock();
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_mutex_lock(&mutex);
	if (ret) assert(false);
#endif
	return 0;
}

static int Mutex_Unlock(MUTEX &mutex)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	LeaveCriticalSection(&mutex);
#else
	mutex.unlock();
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_mutex_unlock(&mutex);
	if (ret) assert(false);
#endif
	return 0;
}

static int Cond_Wait(CONDITION &condition, MUTEX &mutex)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	SleepConditionVariableCS(&condition, &mutex, INFINITE);
#else
	std::unique_lock<std::mutex> locker(mutex, std::defer_lock);
	condition.wait(locker);
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_cond_wait(&condition, &mutex);
	if (ret) assert(false);
#endif
	return 0;
}

static int Cond_Wait_Timeout(CONDITION &condition, MUTEX &mutex, int ms)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	SleepConditionVariableCS(&condition, &mutex, ms);
#else
	std::unique_lock<std::mutex> locker(mutex, std::defer_lock);
	condition.wait_for(locker, std::chrono::milliseconds(ms));
#endif // USE_CRITICAL_SECTION

#else
	timespec timer;
	clock_gettime(CLOCK_REALTIME, &timer);
	long long nsec = timer.tv_nsec + 1000000 * ms;
	timer.tv_sec += nsec / 1000000000;
	timer.tv_nsec = nsec % 1000000000;
	pthread_cond_timedwait(&condition, &mutex, &timer);
#endif
	return 0;
}

static int Cond_Signal_One(CONDITION &condition)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	WakeConditionVariable(&condition);
#else
	condition.notify_one();
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_cond_signal(&condition);
	if (ret) assert(false);
#endif
	return 0;
}

static int Cond_Signal_All(CONDITION &condition)
{
#ifdef _WIN32
#ifdef USE_CRITICAL_SECTION
	WakeAllConditionVariable(&condition);
#else
	condition.notify_all();
#endif // USE_CRITICAL_SECTION
#else
    int ret = pthread_cond_broadcast(&condition);
	if (ret) assert(false);
#endif
	return 0;
}
